﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;

using System.Data.SugarClient.Parser;

namespace System.Data.SugarClient
{

	public class SugarCommand  : DbCommand
	{

		#region Constructors

		public SugarCommand() : this(null)
		{
		}

		public SugarCommand(
			SugarConnection connection)
			: this(string.Empty, connection)
		{
		}

		public SugarCommand(
			string commandText,
			SugarConnection connection)
		{
			this._commandText = commandText;
			this._sugarConnection = connection;
		}

		#endregion

		#region Fields

		private SugarConnection _sugarConnection;
		private string _commandText;
		private SugarParameterCollection _parameters = new SugarParameterCollection();
		private ISugarSoapActionResult _actionResult;

		

		#endregion

		#region Properties

		#region Overrides

		public override string CommandText
		{
			get { return _commandText; }
			set { _commandText = value; }
		}

		public override int CommandTimeout
		{
			get { return this.SoapContext.Timeout;}
			set { this.SoapContext.Timeout = value; }
		}

		public override CommandType CommandType
		{
			get
			{
				return CommandType.Text;
			}
			set
			{
				// nothing to do;
			}
		}

		protected override DbConnection DbConnection
		{
			get { return _sugarConnection; }
			set { this._sugarConnection = value as SugarConnection; }
		}

		protected override DbTransaction DbTransaction
		{
			get
			{
				return null;
			}
			set
			{
				// nothing to do
			}
		}

		public override UpdateRowSource UpdatedRowSource
		{
			get
			{
				return UpdateRowSource.None;
			}
			set
			{
				// nothing to do;
			}
		}
		
		public override bool DesignTimeVisible
		{
			get
			{
				throw new NotImplementedException();
			}
			set
			{
				throw new NotImplementedException();
			}
		}

		protected override DbParameterCollection DbParameterCollection
		{
			get { return _parameters; }
		}

		#endregion

		internal ISugarSoapActionResult ActionResult
		{
			get { return _actionResult; }
		}

		internal SugarSoapContext SoapContext
		{
			get
			{
				return (this._sugarConnection == null) ? null : this._sugarConnection.SoapContext;
			}
		}

		#endregion

		#region Methods

		protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
		{
			return this.BuildDataReader(behavior);
		}

		public override object ExecuteScalar()
		{
			using (IDataReader reader = this.ExecuteReader())
			{
				while (reader.Read()) return reader[0];
				reader.Close();
			}
			return null;
		}

		public override int ExecuteNonQuery()
		{
			Tokenizer tokenizer = new Tokenizer(this.CommandText);
			for (string token = tokenizer.GetString(); !string.IsNullOrEmpty(token); token = tokenizer.GetString())
			{
				switch (token.ToUpper())
				{
					case "INSERT": return this.ProcessInsert();
					case "UPDATE": return this.ProcessUpdate();
					case "DELETE": throw new NotImplementedException();
				}
			}
			throw new NotImplementedException();
		}
		 
		public override void Cancel()
		{
			throw new NotImplementedException();
		}

		protected override DbParameter CreateDbParameter()
		{
			return new SugarParameter();
		}
		
		public override void Prepare()
		{
			throw new NotImplementedException();
		}


		protected SugarDataReader BuildDataReader(CommandBehavior behavior)
		{
			string module = string.Empty;
			string criteria = string.Empty;
			string order = string.Empty;
			List<string> fields = new List<string>();
			bool allfield = false;

			Tokenizer tokenizer = new Tokenizer(this.CommandText);
			int context = 0;
			for (string token = tokenizer.GetString(); !string.IsNullOrEmpty(token); token = tokenizer.GetString())
			{
				switch (token.ToUpper())
				{
					case "SELECT": context = 1; continue;
					case "FROM": context = 2; continue;
					case "JOIN": context = 4; continue;
					case "WHERE": context = 8; continue;
					case "ORDER": context = 16; continue;
				}

				switch (context)
				{
					case 1: // SELECT
						if (",".Equals(token)) continue;
						else if ("*".Equals(token)) allfield = true;
						else fields.Add(token);
						break;
					case 2: // FROM
						module = token;
						break;
					case 4: // JOIN
						throw new SugarException("\"JOIN\" is not supported!", new NotImplementedException());
					case 8: // WHERE
						criteria =
							string.Join(" ",
							new string[] {
								criteria,
								string.Format( 
									(tokenizer.TokenType == TokenType.STRING) ? "{0}'" : "{0}", token) });
						break;
					case 16: // ORDER BY
						if ("BY".Equals(token)) continue;
						order += token; break;
				}
			}

			if (allfield)
			{
				IGetModuleFieldsActionResult aresult = this.SoapContext.CreateAction(SugarSoapActionType.GetModuleFields, module).Execute() as IGetModuleFieldsActionResult;
				foreach (SugarVarDef vardef in aresult.VarDefs) { fields.Add(vardef.Name); }
			}

			if (string.IsNullOrEmpty(order)) order = fields[0];
			if (string.IsNullOrEmpty(criteria)) criteria = "0 = 0";

			ISugarSoapAction action =
				this.SoapContext.CreateAction(
					SugarSoapActionType.GetEntryList,
					new object[]{
						module,													// module
						criteria,												// criteria
						order,													// order,
						0,															// offset,
						fields.ToArray(),								// fields,
						this._sugarConnection.PageSize,	// limit,
						false														// delete
					});

			IGetEntryListActionResult result = action.Execute() as IGetEntryListActionResult;

			this._actionResult = result;

			SugarDataReader reader =
				new SugarDataReader(result);

			return reader;
		}

		protected int ProcessInsert()
		{
			string module = string.Empty;
			List<string> fields = new List<string>();
			List<string> values = new List<string>();
			Tokenizer tokenizer = new Tokenizer(this.CommandText);
			int context = 0;
			for (string token = tokenizer.GetString(); !string.IsNullOrEmpty(token); token = tokenizer.GetString())
			{
				switch (token.ToUpper())
				{
					case "INSERT": context = 1; continue;
					case "INTO": context = 2; continue;
					case "VALUES": context = 4; continue;
				}

				switch (context)
				{
					case 1: // INSERT
						break;
					case 2: // INTO
						if ("(".Equals(token)) continue;
						if (",".Equals(token)) continue;
						if (")".Equals(token)) continue;
						if(string.IsNullOrEmpty(module)) module = token;
						else fields.Add(token);
						break;
					case 4: // VALUES
						if ("(".Equals(token)) continue;
						if (",".Equals(token)) continue;
						if (")".Equals(token)) continue;
						if (tokenizer.TokenType == TokenType.VARIABLE) values.Add(this.Parameters["@" + token].Value.ToString());
						else values.Add(token);
						break;
				}
			}

			ISugarSoapAction action = this.SoapContext.CreateAction(SugarSoapActionType.SetEntry, module, ToDictionary(fields, values));
			ISetEntryActionResult result = action.Execute() as ISetEntryActionResult;

			this._actionResult = result;
			if (result.Success) return 1;
			else return 0;
			// return 1;
		}

		protected int ProcessUpdate()
		{
			string module = string.Empty;
			List<string> fields = new List<string>();
			List<string> values = new List<string>();
			Tokenizer tokenizer = new Tokenizer(this.CommandText);
			int context = 0;
			int operand = -1;
			for (string token = tokenizer.GetString(); !string.IsNullOrEmpty(token); token = tokenizer.GetString())
			{
				switch (token.ToUpper())
				{
					case "UPDATE": context = 1; continue;
					case "SET": context = 2; continue;
					case "WHERE": context = 4; continue;
				}

				switch (context)
				{
					case 1: // UPDATE
						module = token;
						break;
					case 2: // SET
						if (",".Equals(token)) { operand *= -1; continue; }
						if ("=".Equals(token)) { operand *= -1; continue; }
						if (operand < 0)
						{
							fields.Add(token);
						}
						else
						{
							if (tokenizer.TokenType == TokenType.VARIABLE) values.Add(this.Parameters["@" + token].Value.ToString());
							else values.Add(token);
						}
						break;
					case 4: // WHERE
						break;
				}
			}

			ISetEntryActionResult result =
				this.SoapContext.ExecuteAction(SugarSoapActionType.SetEntry, module, ToDictionary(fields, values)) as ISetEntryActionResult;

			this._actionResult = result;

			if (result.Success) return 1;
			else return 0;
			// return 1;
		}

		protected IDictionary<string, object> ToDictionary(
			IList<string> fields,
			IList<string> values)
		{
			Dictionary<string, object> newvalues = new Dictionary<string, object>();
			for (int i = 0; i < fields.Count; ++i)
			{
				newvalues[fields[i]] = values[i];
			}
			return newvalues;
		}

		#endregion

	}
}
